package com.feshfans.byteconvert.data;

import com.feshfans.byteconvert.annotation.Bytes;
import com.feshfans.byteconvert.annotation.Endian;
import com.feshfans.byteconvert.convertor.*;
import ognl.Ognl;
import ognl.OgnlContext;
import ognl.OgnlException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class ConvertData<T> {

    private static final Logger Log = LoggerFactory.getLogger(ConvertData.class);

    private static final Map<String,Map<Integer, BytesEntity>> clsFieldMap = new HashMap<>();
    private static final Map<String,ClsEntity> clsMap = new HashMap<>();

    public ConvertData() throws OgnlException {
        parseClsAndField();
    }

    public void fromBytes(byte[] bytes) throws OgnlException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {

        Map<Integer,BytesEntity> fieldMap = clsFieldMap.get(this.getClass().getName());

        int count = bytes.length;
        int fieldCount = fieldMap.size();
        OgnlContext ognlContext = new OgnlContext();

        byte[] copy = Arrays.copyOf(bytes,count);
        int position = 0;
        for(int i=1;i<=fieldCount;i++){

            BytesEntity entity = fieldMap.get(i);
            Field field = entity.getField();
            int length = entity.length;
            if(!entity.isValidOfLength){
                length =(Integer) Ognl.getValue(entity.ognlExpr,ognlContext);
            }
            byte[] fieldBytes = new byte[length];
            System.arraycopy(copy,position,fieldBytes,0,length);

            Class fieldCls = field.getType();
            if(fieldCls.isAssignableFrom(Byte.TYPE) || fieldCls.isAssignableFrom(Byte.class)){
                ByteConvertor convertor = new ByteConvertor();
                byte b = convertor.fromBytes(fieldBytes);
                field.setByte(this,b);
            }
            if(fieldCls.isAssignableFrom(Short.TYPE) || fieldCls.isAssignableFrom(Short.class)){
                ShortConvertor convertor = new ShortConvertor();
                short s = convertor.fromBytes(fieldBytes);
                field.setShort(this,s);
            }
            if(fieldCls.isAssignableFrom(Integer.TYPE)|| fieldCls.isAssignableFrom(Integer.class)){
                IntConvertor convertor = new IntConvertor();
                int value = convertor.fromBytes(fieldBytes);
                field.setInt(this,value);
            }
            if(fieldCls.isAssignableFrom(Long.TYPE) || fieldCls.isAssignableFrom(Long.class)){
                LongConvertor convertor = new LongConvertor();
                Long value = convertor.fromBytes(fieldBytes);
                field.setLong(this,value);
            }
            if(BigInteger.class.isAssignableFrom(fieldCls)){
                System.out.println("biginteger");
            }
            if(BigDecimal.class.isAssignableFrom(fieldCls)){
                System.out.println("bigdecimail");

            }
            if(String.class.isAssignableFrom(fieldCls)){
                StringConvertor convertor = new StringConvertor();
                String value = convertor.fromBytes(fieldBytes);
                field.set(this,value);
            }
            if(ConvertData.class.isAssignableFrom(fieldCls)){
                Object obj = fieldCls.newInstance();
                Method method = fieldCls.getMethod("fromBytes",byte[].class);
                method.invoke(obj,fieldBytes);
            }
            if(List.class.isAssignableFrom(fieldCls)){
                System.out.println("list");
            }
            position += length;
            ognlContext.put(field.getName(),field.get(this));

        }
    }

    public byte[] toBytes(){

        Map<Integer,BytesEntity> fieldMap = clsFieldMap.get(this.getClass().getName());
        int fieldCount = fieldMap.size();
        for(int i=1;i<= fieldCount;i++){
            BytesEntity entity = fieldMap.get(i);
            Field field = entity.getField();

        }

        return null;

    }

    private Map<Integer,BytesEntity> parseClsAndField() throws OgnlException {
        Class cls = this.getClass();
        ClsEntity clsEntity = getCls(cls);
        Map<Integer,BytesEntity> fieldMap = getFiled(cls);
        return fieldMap;
    }

    private ClsEntity getCls(Class cls){

        if(!clsMap.containsKey(cls.getName())){
            Endian.EndianType endianType = Endian.EndianType.Little;
            Annotation endian = cls.getAnnotation(Endian.class);
            if(endian == null ){
                endianType = Endian.EndianType.Little;
            } else {
                endianType = ((Endian)endian).type();
            }

            ClsEntity entity = new ClsEntity();
            entity.endianType = endianType;

            clsMap.put(cls.getName(),entity);
        }
        return clsMap.get(cls.getName());

    }
    private Map<Integer,BytesEntity> getFiled(Class cls) throws OgnlException {
        if(!clsFieldMap.containsKey(cls.getName())){
            Map<Integer,BytesEntity> fieldMap = new HashMap<>();
            Field[] fields = cls.getDeclaredFields();
            for(Field field : fields){
                field.setAccessible(true);
                Bytes bytes1 = field.getAnnotation(Bytes.class);
                if(bytes1 == null){
                    Log.debug("{}:{} 没有 bytes 注解，忽略",cls.getName(),field.getName());
                    continue;
                }
                int sort = bytes1.sort();
                if(fieldMap.containsKey(sort)){
                    String errorMsg = ""+cls.getName()+":"+field.getName()+",sort 与其它字段 sort 重复";
                    throw new IllegalArgumentException(errorMsg);
                }
                BytesEntity entity = new BytesEntity();
                entity.setField(field);
                entity.setSort(sort);
                entity.setLength(bytes1.length());
                entity.setLengthExpr(bytes1.lengthExpr());
                check(entity);

                fieldMap.put(sort,entity);
            }
            clsFieldMap.put(cls.getName(),fieldMap);
        }
        return clsFieldMap.get(cls.getName());
    }

    private String check(BytesEntity entity) throws OgnlException {

        int sort = entity.getSort();
        int length = entity.getLength();
        String lengthExpr = entity.getLengthExpr();

        if(sort<= 0){
            return "sort 不能小于 1";
        }
        if(length <= 0 && (lengthExpr == null || lengthExpr.trim().length() == 0)){
            return "length 和 lengthExpr 二选一。length 范围 : 0-"+Integer.MAX_VALUE;
        }
        if(length <= 0 ){ // 说明 ognl 表达式生效
            Object ognlExpr = Ognl.parseExpression(lengthExpr);
            entity.isValidOfLength = false;
            entity.ognlExpr = ognlExpr;
            return null;
        } else {
            entity.isValidOfLength = true;
            return null;
        }
    }

    static class BytesEntity{
        private int sort;
        private int length;
        private String lengthExpr;
        private boolean isValidOfLength;
        private Object ognlExpr;

        private Field field;

        public int getSort() {
            return sort;
        }

        public void setSort(int sort) {
            this.sort = sort;
        }

        public int getLength() {
            return length;
        }

        public void setLength(int length) {
            this.length = length;
        }

        public String getLengthExpr() {
            return lengthExpr;
        }

        public void setLengthExpr(String lengthExpr) {
            this.lengthExpr = lengthExpr;
        }

        public Field getField() {
            return field;
        }

        public void setField(Field field) {
            this.field = field;
        }
    }

    static class ClsEntity {
        private Endian.EndianType endianType;
    }
}
